/**
 *  @file SSLServerSocket.cpp
 */
#include "SSLServerSocket.h"
#include "SSLSocket.h"
#include "SSLSocketException.h"
#include "../../common/CppMemPool.h"

namespace cppflib
{

namespace net
{

namespace ssl
{


SSLServerSocket::SSLServerSocket(void)
{
   this->pSSLCtx = NULL;
   this->pPeerCertCallback = NULL;
}

SSLServerSocket::SSLServerSocket(SSLContext *pSSLCtx, ISSLPeerCertCallback *pPeerCertCb)
{
   this->pSSLCtx = pSSLCtx;
   cppmempool::Retain(this->pSSLCtx);
   this->pPeerCertCallback = pPeerCertCb;
   cppmempool::Retain(this->pPeerCertCallback);
}

SSLServerSocket::~SSLServerSocket(void)
{
   Close();

   if (pPeerCertCallback) {
      cppmempool::Release(pPeerCertCallback);
      pPeerCertCallback = NULL;
   }

   if (pSSLCtx) {
      cppmempool::Release(pSSLCtx);
      pSSLCtx = NULL;
   }
}

/////////////////// Methods present in Socket but not supported by SSLServerSocket /////////////////

bool SSLServerSocket::Create(const CFString &socketAddress, u16_t socketPort, Socket::SocketType socketType)
{
   throw SSLSocketException(_S("Don't call this version of 'Create' from a SSLServerSocket"));
   return false;
}

bool SSLServerSocket::Connect(const CFString &hostAddress, u16_t hostPort)
{
   throw SSLSocketException(_S("Don't call 'Connect' from a SSLServerSocket"));
   return false;
}

bool SSLServerSocket::Connect(const CFString &hostAddress, u16_t hostPort, int timeout)
{
   throw SSLSocketException(_S("Don't call 'Connect' from a SSLServerSocket"));
   return false;
}

bool SSLServerSocket::Accept(Socket &connectedSocket)
{
   throw SSLSocketException(_S("Don't call this version of 'Accept' from a SSLServerSocket"));
   return false;
}

int SSLServerSocket::ReceiveFrom(u8_t * pBuf, int bufLen, int nFlags, CFString &peerAddress, u16_t &peerPort)
{
   throw SSLSocketException(_S("Don't call 'ReceiveFrom' from a SSLServerSocket"));
   return 0;
}

int SSLServerSocket::ReceiveFromTimeout(int timeout, u8_t * pBuf, int bufLen, int nFlags, CFString &peerAddress, u16_t &peerPort)
{
   throw SSLSocketException(_S("Don't call 'ReceiveFromTimeout' from a SSLServerSocket"));
   return 0;
}

int SSLServerSocket::SendTo(const u8_t * pBuf, int bufLen, int nFlags, const CFString &peerAddress, u16_t peerPort)
{
   throw SSLSocketException(_S("Don't call 'SendTo' from a SSLServerSocket"));
   return 0;
}
//////////////////// End of not supported methods /////////////////////

/**
 *  SSLServerSocket is supposed to be based on TCP only
 */
bool SSLServerSocket::Create(const CFString &socketAddress, u16_t socketPort)
{
   if (!Socket::Create(socketAddress, socketPort, Socket::Stream))
      throw SSLSocketException(_S("Create socket failed"));
   
   return true;
}


/**
 *  Accept and return a new SSL socket.
 *  The returned SSL socket has to call 'WaitHandshake'
 *
 *  @param [in] pPeerCertCb -- callback to peek at the certificates received. 
 *                             Can be NULL if not interested
 */
SSLSocket *SSLServerSocket::Accept(ISSLPeerCertCallback *pPeerCertCb)
{
   Socket::sockattr_t flag = pSSLCtx->NeedPeerAuthentication() ? 
                                  static_cast<Socket::sockattr_t>(Socket::SSL_CHK_PEERCERT | Socket::SSL_CHK_VERIFYRESULT) :
                                  static_cast<Socket::sockattr_t>(Socket::SSL_CHK_VERIFYRESULT);
   SSLSocket *pSSLSock = new SSLSocket(flag);

   if (!Socket::Accept(*pSSLSock))
      throw SSLSocketException(_S("Accept SSL socket error"));

   pSSLCtx->CreateClientSocket(pSSLSock, pPeerCertCb);

   return pSSLSock;

}

/**
 *  Use the internal peer cert callback if any
 */
Socket *SSLServerSocket::Accept()
{
   return SSLServerSocket::Accept(this->pPeerCertCallback);
}


} // end of namespace ssl

} // end of namespace net

} // end of namespace cppflib
